#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <getopt.h>

#include <sys/socket.h>
#include <sys/time.h>

#include <arpa/inet.h>

#define MAX_MAS_LENGTH 512
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)

int SendMsg(int connfd, char *msg, int length)
{

	int res = send(connfd, msg, length, 0);
	if (res < 0)
	{
		perror("send");
		exit(1);
	}
	return res;
}

//
int RecvMsg(int connfd, char *msg, int length)
{

	int res = recv(connfd, msg, length, 0);
	if (res < 0)
	{
		perror("recv");
		exit(1);
	}
	return res;
}

void Equals(char *pattern, char *result, char *casename)
{

	if (strcmp(pattern, result) == 0)
	{
		// printf("==> PASS --> %s\n", casename);
	}
	else
	{
		printf("==> FAILED --> %s, '%s' != '%s'\n", casename, pattern, result);
	}
}

// "SET Name Huiyuan" "Success"
void TestCase(int connfd, char *msg, char *pattern, char *casename)
{

	if (!msg || !pattern || !casename)
		return;

	SendMsg(connfd, msg, strlen(msg));

	char result[MAX_MAS_LENGTH] = {0};
	RecvMsg(connfd, result, MAX_MAS_LENGTH);

	Equals(pattern, result, casename);
}

void ArrayTestCase(int connfd)
{

	TestCase(connfd, "SET Name Huiyuan", "Success", "SETCase");
	TestCase(connfd, "GET Name", "Huiyuan", "GETCase");
	TestCase(connfd, "MOD Name MiXueBinChen", "Success", "MODCase");
	TestCase(connfd, "GET Name", "MiXueBinChen", "GETCase");
	TestCase(connfd, "DEL Name", "Success", "DELCase");
	TestCase(connfd, "GET Name", "Failed", "GETCase");
}

void ArrayTestCase10W(int connfd)
{ // 10w

	int count = 100000;
	int i = 0;

	while (i++ < count)
	{
		ArrayTestCase(connfd);
	}
}

void RbtreeRestcase(int connfd)
{

	TestCase(connfd, "RSET Name Huiyuan", "Success", "SETCase");
	TestCase(connfd, "RGET Name", "Huiyuan", "GETCase");
	TestCase(connfd, "RMOD Name Kenan", "Success", "MODCase");
	TestCase(connfd, "RGET Name", "Kenan", "GETCase");
	TestCase(connfd, "RDEL Name", "Success", "DELCase");
	TestCase(connfd, "RGET Name", "Failed", "GETCase");
}

void RbtreeRestcase10W(int connfd)
{ // 10w

	int count = 100000;
	int i = 0;

	while (i++ < count)
	{
		ArrayTestCase(connfd);
	}
}

void RbtreeRestcase5WNode(int connfd)
{

	int count = 10;
	int i = 0;

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "RSET Name%d Huiyuan%d", i, i);
		TestCase(connfd, cmd, "Success", "SETCase");

		char result[128] = {0};
		sprintf(result, "%d", i + 1);
		TestCase(connfd, "RCOUNT", result, "RCOUNT");
	}

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "RDEL Name%d Huiyuan%d", i, i);
		TestCase(connfd, cmd, "Success", "DELCase");

		char result[128] = {0};
		sprintf(result, "%d", count - (i + 1));
		TestCase(connfd, "RCOUNT", result, "RCOUNT");
	}
}

void HashTestCase(int connfd)
{

	TestCase(connfd, "HSET Name Huiyuan", "Success", "HSETCase");
	TestCase(connfd, "HGET Name", "Huiyuan", "HGETCase");
	TestCase(connfd, "HMOD Name Kenan", "Success", "HMODCase");
	TestCase(connfd, "HGET Name", "Kenan", "HGETCase");
	TestCase(connfd, "HDEL Name", "Success", "HDELCase");
	TestCase(connfd, "HGET Name", "Failed", "HGETCase");
}

void HashTestCase10W(int connfd)
{ // 10w

	int count = 100000;
	int i = 0;

	while (i++ < count)
	{
		HashTestCase(connfd);
	}
}

void HashTestCase5WNode(int connfd)
{

	int count = 500000;
	int i = 0;

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "HSET Name%d Huiyuan%d", i, i);
		TestCase(connfd, cmd, "Success", "SETCase");

		char result[128] = {0};
		sprintf(result, "%d", i + 1);
		TestCase(connfd, "HCOUNT", result, "HCOUNT");
	}

	for (i = 0; i < count; i++)
	{

		char cmd[128] = {0};

		snprintf(cmd, 128, "HDEL Name%d Huiyuan%d", i, i);
		TestCase(connfd, cmd, "Success", "DELCase");

		char result[128] = {0};
		sprintf(result, "%d", count - (i + 1));
		TestCase(connfd, "HCOUNT", result, "RCOUNT");
	}
}

int ConnectTcpserver(const char *ip, unsigned short port)
{

	int connfd = socket(AF_INET, SOCK_STREAM, 0);

	struct sockaddr_in tcpserver_addr;
	memset(&tcpserver_addr, 0, sizeof(struct sockaddr_in));

	tcpserver_addr.sin_family = AF_INET;
	tcpserver_addr.sin_addr.s_addr = inet_addr(ip);
	tcpserver_addr.sin_port = htons(port);

	int ret = connect(connfd, (struct sockaddr *)&tcpserver_addr, sizeof(struct sockaddr_in));
	if (ret)
	{
		perror("connect");
		return -1;
	}

	return connfd;
}

// array: 0x01, rbtree: 0x02, hash: 0x03

// ./kbox_test -s 192.168.196.129 -p 9096 -m 1
int main(int argc, char *argv[])
{

	int ret = 0;

	char ip[16] = {0};
	int port = 0;
	int mode = 1;

	int opt;
	while ((opt = getopt(argc, argv, "s:p:m:?")) != -1)
	{

		switch (opt)
		{

		case 's':
			strcpy(ip, optarg);
			break;

		case 'p':
			port = atoi(optarg);
			break;

		case 'm':
			mode = atoi(optarg);
			break;

		default:
			return -1;
		}
	}

	int connfd = ConnectTcpserver(ip, port);

	if (mode & 0x1)
	{ // array

		struct timeval tv_begin;
		gettimeofday(&tv_begin, NULL);

		ArrayTestCase10W(connfd);

		struct timeval tv_end;
		gettimeofday(&tv_end, NULL);

		int time_used = TIME_SUB_MS(tv_end, tv_begin);

		printf("array testcase--> time_used: %d, qps: %.2lf\n", time_used, 600000.0 / (time_used / 1000));
	}

	if (mode & 0x2)
	{ // rbtree

		struct timeval tv_begin;
		gettimeofday(&tv_begin, NULL);

		RbtreeRestcase10W(connfd);

		struct timeval tv_end;
		gettimeofday(&tv_end, NULL);

		int time_used = TIME_SUB_MS(tv_end, tv_begin);

		printf("rbtree testcase-->  time_used: %d, qps: %.2lf\n", time_used, 600000.0 / (time_used / 1000));
	}

	if (mode & 0x3)
	{ // hash

		struct timeval tv_begin;
		gettimeofday(&tv_begin, NULL);

		HashTestCase10W(connfd);

		struct timeval tv_end;
		gettimeofday(&tv_end, NULL);

		int time_used = TIME_SUB_MS(tv_end, tv_begin);

		printf("hash testcase-->  time_used: %d, qps: %.2lf\n", time_used, 600000.0 / (time_used / 1000));
	}
}
